package com.new4net.exe_stream.meta.impl;


import com.new4net.exe_stream.instruction.impl.*;
import com.new4net.exe_stream.meta.*;

import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

public abstract class AbstractExecutionStatement<T> implements ExecutionStatement<T> {
    protected ExecutionInstruction executionInstruction;
    
    protected ExecutionContext context;


    public AbstractExecutionStatement(String name, ExecutionContext context) {

        this.context = context;
        this.executionInstruction = new ExecutionInstruction(name);
    }



    @Override
    public <R> ThenStatement<ExecutionStatement<T>,R>
        _then(String title, Function<ExecutionContext.InstructionContext,R> fun) {
        ThenInstruction<R> thenInstruction = new ThenInstruction<>(title, fun);

        return new ThenStatement<ExecutionStatement<T>,R>() {
            @Override
            public  ExecutionStatement<T> _catch(BiFunction<Throwable, ExecutionContext.InstructionContext, R> fun) {
                thenInstruction.setCathFunction(fun);
                executionInstruction.addInstruction(thenInstruction);
                return  AbstractExecutionStatement.this;
            }

            @Override
            public ExecutionStatement<T> _end() {
                executionInstruction.addInstruction(thenInstruction);
                return  AbstractExecutionStatement.this;
            }
        };
    }

    @Override
    public <R> SwitchStatement<ExecutionStatement<T>,R> _switch(String title) {
        SwitchInstruction switchInstruction = new SwitchInstruction<R>(title);

        return new SwitchStatement<ExecutionStatement<T>,R>() {
            @Override
            public ExecutionStatement<T> _end() {
                executionInstruction.addInstruction(switchInstruction);
                return AbstractExecutionStatement.this;
            }

            @Override
            public IfStatement<SwitchStatement<ExecutionStatement<T>,R>,R> _if(String title, Predicate<ExecutionContext.InstructionContext> predicate, Function<ExecutionContext.InstructionContext, R> fun) {
                switchInstruction.setIfContent(title,predicate,fun);

                SwitchStatement<ExecutionStatement<T>,R> switchStatement = this;

                return new IfStatement<SwitchStatement<ExecutionStatement<T>,R>,R>() {
                    @Override
                    public  IfStatement<SwitchStatement<ExecutionStatement<T>,R>,R> _else_if(String title, Predicate<ExecutionContext.InstructionContext> predicate, Function<ExecutionContext.InstructionContext, R> fun) {
                        switchInstruction.addElseIfContent(title,predicate,fun);

                        return this;
                    }

                    @Override
                    public  SwitchStatement<ExecutionStatement<T>,R> _else(String title, Function<ExecutionContext.InstructionContext, R> fun) {
                        switchInstruction.setElseContent(title,fun);

                        return  switchStatement;
                    }

                    @Override
                    public SwitchStatement<ExecutionStatement<T>,R> _end() {
                        return switchStatement;
                    }
                };
            }


        };
    }






    @Override
    public <R> ExecutionStatement<T> _while(String title, Consumer<ExecutionContext.InstructionContext> consumer, Predicate<ExecutionContext.InstructionContext> predicate, Function<ExecutionContext.InstructionContext,R> fun) {
        executionInstruction.addInstruction(new WhileInstruction<R>(title,consumer,predicate,fun));

        return this;
    }

    @Override
    public <R> ExecutionStatement<T> _do_while(String title, Consumer<ExecutionContext.InstructionContext> consumer, Function<ExecutionContext.InstructionContext,R> fun, Predicate<ExecutionContext.InstructionContext> predicate) {
        executionInstruction.addInstruction(new DoWhileInstruction<R>(title,consumer,fun,predicate));

        return this;
    }

    @Override
    public ParallelStatement<ExecutionStatement<T>> _parallel() {
        ParallelInstruction parallelInstruction = new ParallelInstruction();

        ParallelStatement<ExecutionStatement<T>> parallelStatement = new ParallelStatement<ExecutionStatement<T>>() {
            @Override
            public ExecutionStatement<ParallelStatement<ExecutionStatement<T>>> _create_new(String name) {
                ParallelStatement<ExecutionStatement<T>> pthat =  this;

                String newName = executionInstruction.getInstructionName() + "/" + name;
                return new AbstractExecutionStatement<ParallelStatement<ExecutionStatement<T>>>(newName, null) {
                    @Override
                    public ParallelStatement<ExecutionStatement<T>> _end() {

                        parallelInstruction.addExecutionInstruction(this.executionInstruction);
                        return pthat;
                    }
                };
            }

            @Override
            public ExecutionStatement<T> await() {
                parallelInstruction.setNeedWait(true);
                executionInstruction.addInstruction(parallelInstruction);
                return  AbstractExecutionStatement.this;
            }

            @Override
            public ExecutionStatement<T> _end() {
                executionInstruction.addInstruction(parallelInstruction);
                return  AbstractExecutionStatement.this;
            }
        };
        return parallelStatement;
    }


}
